Skip to content

feat(web): show thread API cost in composer - #9136

Open
saphid wants to merge 162 commits into
pingdotgg:mainfrom
saphid:agent/composer-thread-cost
Open

feat(web): show thread API cost in composer#9136
saphid wants to merge 162 commits into
pingdotgg:mainfrom
saphid:agent/composer-thread-cost

Conversation

@saphid

@saphid saphid commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

What Changed

The chat composer now shows the estimated API cost attributed to the current thread, with cache-write, cache-read, and fresh-token components. Attribution follows current and historical provider sessions and dedicated worktrees, and the client asks for a filtered breakdown only when the server advertises that capability.

Why

The Usage page can explain aggregate spend, but it does not answer the question while a conversation is open. This indicator makes the thread's accumulated cost available in context without fully parsing every unrelated Codex transcript: cold reads inspect bounded identity metadata and parse matching candidates, while a compatible warm summary reuses its immutable records.

Stack and Review Scope

This PR is cumulative and lands after #9019. Review the composer-cost increment; #9308 includes it.

Verification

Current candidate 089236fb8c2e6910d082bf726b3e30636925c8e0 includes upstream main 39802c06117fae0b3da43624b0d54309c5437c72. The upstream import integration preserves usage resume-cursor history and atomic imported-transcript recording, including insert-conflict options. Focused provider-directory, UsageService and composer-cost tests passed (46 tests); server package typecheck and changed-file lint passed. Two history fixtures were updated to include the required provider instance, and repository failure doubles now implement the import method.

A fresh direct claude --model claude-opus-5 --effort high --permission-mode plan --tools Read,Grep,Glob --print … review attempt exited 1 because the OAuth session expired; no Claude review occurred for this merge. Earlier evidence below retains its original revision and scope.

Candidate: 089236fb8c2e6910d082bf726b3e30636925c8e0, including upstream main 82c2b7ffb4d450572f5baf9a70693f1e25cfdfed.

  • The full focused contribution check at ba873b8 passed 127 tests across 8 files, plus the affected package typechecks.
  • The final 82c2b7f delta removes an unused pricing helper: 11 pricing tests, server typecheck, Knip and git diff --check passed on the pre-repair main composition. The chart/runtime contribution was unchanged by that final delta.
  • The chart merge preserves upstream's live column builder and this PR's daily/hourly and range behavior. Fresh independent source review of the shared resolution found no actionable issues.
Focused commands
vp test run apps/server/src/provider/Layers/ProviderSessionDirectory.test.ts apps/server/src/usage/UsageService.test.ts apps/server/src/usage/usageScanCache.test.ts apps/server/src/usage/usageThreads.test.ts apps/server/src/usage/usageTranscriptReader.test.ts apps/web/src/components/chat/ThreadCostIndicator.test.tsx apps/web/src/state/threadCost.test.ts apps/web/src/components/usage/UsageProviderChart.test.ts
vp run --filter t3 typecheck
vp run --filter @t3tools/web typecheck
vp run --filter @t3tools/contracts typecheck
vp test run apps/server/src/usage/usagePricing.test.ts
vp run --filter t3 typecheck

Refresh correctness checks

The refresh repair passed the focused UsageService suite (26 tests), 3 chart-gesture regression tests, and 8 web usage-state tests. After the final thread-cache guard, 2 affected service tests were rerun and passed; the earlier full-suite count is not presented as a new full-suite run. The final targeted-refresh correction passed 4 affected tests, proving fresh selected-thread costs without parsing unrelated transcript bodies. The optional thread-refresh contract passed 2 schema tests and the contracts package typecheck. The standalone composer-cost hook passed 7 focused tests, including debounce and scope isolation. Server and web package typechecks, changed-file formatting/lint and diff checks passed. Independent source review completed; its findings were verified against the frozen source and no actionable issue remains.

These repairs cancel a chart gesture if its date window changes, let each environment progress after its own pricing request, and coalesce concurrent tokened summary scans. Thread-enabled branches await their environment summary before refreshing thread rows. The source-scan revision guard prevents older aggregation from pruning files discovered by a newer scan. Integrated web evidence and remaining client limits are described below.

Standalone composer cost refresh now sends a new optional thread-read token, so a completed turn can bypass a warm source snapshot.

UI Changes

The integrated composer shows $37.02 for checkout, matching its Usage thread row. Opening cost details shows $13.50 cache writes, $2.52 cache reads and $21.00 fresh input/output; Escape closes the details. threadCost.ts and ThreadCostIndicator.tsx are byte-identical between this PR and the integrated capture, making the UI proof directly reusable. The later server ledger differs; this PR’s own focused server tests remain listed above.

Before: main 39802c06117fae0b3da43624b0d54309c5437c72; after: cumulative #9308 7e248026469ec7be6d5c2bdaccf86f380e0c2476. Both show the same synthetic checkout conversation and viewport. The synthetic thread creation date was corrected to precede its historical transcripts before this capture; no application source changed.

Before: no thread cost indicator

After: thread cost and component breakdown

Clean composer-cost recording · Annotated composer-cost recording. Idle time is trimmed from31.60s to7.30s. Packet validation passed; uploaded media were retrieved and hash-verified.

This is cumulative web integration evidence, not a separate individual-head or native-client recording. No real provider turn was launched; completed-turn refresh remains established by the focused hook/server tests, not this video. No billing or performance measurement is claimed.

Checklist

  • The intended composer-cost increment is focused; its cumulative dependencies are identified above.
  • I explained what changed and why.
  • A current-head composer screenshot is attached; this gap is described above.
  • Integrated cost-detail open/close recording is attached; actual-turn refresh remains test-backed.

Implemented and reviewed with GPT-6 and GPT-5.6 Sol in the Codex harness. A direct Claude Opus 5 high availability attempt exited 1 because OAuth had expired; no Opus review occurred.

Note

Add thread-level usage breakdown, project attribution, and cache-write TTL pricing

  • Adds a thread usage breakdown feature end-to-end: new serverGetUsageThreadBreakdown WebSocket RPC, UsageService.readThreadBreakdown service method, UsageThreadTable and UsageThreadDailyChart web components, useThreadCost hook, and mobile useUsage refresh support. Requests accept date/time windows, project and provider filters, and a refresh token to bypass cached source snapshots.
  • Introduces project attribution by tracking a cwd field on every usage record, resolving it against project and thread-worktree roots via makeProjectResolver, and exposing project slices in merged usage results. Repository read failures degrade to unknown attribution instead of failing the scan.
  • Overhauls cache-write pricing: parses long-context rate tiers from LiteLLM entries, prices five-minute and one-hour cache-creation tokens separately with clamping to total cache creation, and reports cache-write cost as null when any contributing bucket lacks model pricing.
  • Replaces per-file progressive deduplication so the latest snapshot for a given key wins (in both UsageAggregator and ThreadUsageAccumulator), and bumps the scan cache format to version 7 with new cwd, TTL counter, and ScanIdentityCache fields.
  • Risk: scan cache format changes from version 6 to 7 in usageScanCache.ts; older caches are discarded on decode. UsageAggregator.add now returns window membership instead of a newly-counted boolean, and parseClaudeLine returns the first iteration record only — callers needing all iterations must call parseClaudeLineRecords.

Macroscope summarized 089236f.

saphid and others added 21 commits September 1, 2026 21:39
The usage window was locked to four presets, so a spike on the chart
could not be inspected without eyeballing dates. Dragging across any
daily chart now commits the selection as the date window, double-click
returns to the preset, and date fields beside the presets accept any
custom range directly. The server already accepted arbitrary day
windows; this is web-only.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Cap custom windows before enumeration, keep date controls reachable in compact layouts, and complete brush gestures synchronously through pointer capture.
The usage page mixed every session on the machine into one pool, so
there was no way to see which project the spend belonged to. Buckets now
carry the title of the T3 project whose folder the session ran in
(resolved per environment from the transcript cwd, including sessions
driven outside T3 Code), the breakdown gains a Project view, and a
project picker narrows the whole page to one project. Sessions outside
every project group under "Outside projects"; Grok logs record no
folder and always count there. Older environments merge unchanged with
their buckets unattributed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add an on-demand thread breakdown RPC and a web Thread view with total daily cost and Claude subagent slices. Keep lower-cost usage in provider/project remainder rows so the bounded payload still reconciles. Cache-write component accounting remains a separate follow-up.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A thread row in the usage drill-down named the work but offered no way
to get to it. Rows that map to a T3 thread now carry a link that opens
the thread in the app; unattributed session rows stay link-free since
there is nothing to open.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:XXL 1,000+ changed lines (additions + deletions). labels Sep 2, 2026
Comment thread apps/server/src/usage/usageTranscripts.ts
Comment thread apps/web/src/components/usage/UsagePage.tsx
Comment thread apps/web/src/components/chat/ChatComposer.tsx
Comment thread apps/server/src/usage/usageTranscriptReader.ts
Comment thread apps/web/src/components/chat/ThreadCostIndicator.tsx Outdated
Comment thread apps/web/src/state/threadCost.ts Outdated
Comment thread apps/web/src/components/usage/UsagePage.tsx
Comment thread apps/web/src/components/usage/UsageThreadTable.tsx Outdated

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread apps/mobile/src/features/usage/UsageRouteScreen.tsx

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 9170d50. Configure here.

Comment thread apps/server/src/usage/UsageService.ts Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL 1,000+ changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant